﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Collections;
using System.Collections.ObjectModel;
using System.Xml;
using System.Xml.XPath;

using CommonLib;
using log4net;

namespace ConfigUtilLib
{
    public class FetcherAndProcessorBin
    {
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        public string Type { get; private set; }
        public string Dll { get; private set; }

        public FetcherAndProcessorBin()
        {
        }

        public FetcherAndProcessorBin(string type, string dll)
        {
            Type = type;
            Dll = dll;
        }
    }

    public class GeneralConfig
    {
      
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        public const string KickoffIntervalName = "kickoffInterval";
        public const string DebugPortName       = "debugPort";
        public const string FetcherBinaryName = "./FetcherBinay";
        public const string ProcesorBinaryName = "./ProcessorBinay";
        public const string TypeName = "type";
        public const string DllName = "DLL";



        public long KickoffInterval;
        public long BackdoorPort;

        private Dictionary<string, FetcherAndProcessorBin> _fetcherBinConfigList = new Dictionary<string, FetcherAndProcessorBin>();
        private Dictionary<string, FetcherAndProcessorBin> _processorBinConfigList = new Dictionary<string, FetcherAndProcessorBin>();
        

        /// <summary>
        ///    Reading Configuration General Configuration
        /// </summary>
        /// <param name="configNode"></param>
        /// <returns></returns>
        public bool  LoadConfiguration(XmlNode configNode)
        {
            bool retVal = true;

            log.DebugFormat("Started Reading General Configuration Section..");
            XmlNode kickOffIntrlNode = configNode.SelectSingleNode(KickoffIntervalName);
            XmlNode backdoorPortNode = configNode.SelectSingleNode(DebugPortName);

            if( kickOffIntrlNode == null   )
            {
                Console.Error.WriteLine(" kickOffInterval : Missing ..");
                retVal = false;
            }

            if (backdoorPortNode == null)
            {
                Console.Error.WriteLine(" Backdoor port  : Missing ..");
            }

            KickoffInterval = System.Convert.ToInt64(kickOffIntrlNode.InnerText);
            BackdoorPort = System.Convert.ToInt64(backdoorPortNode.InnerText);

            #region READ_FETCHER_BIN

            XmlNodeList fetcherBinList = configNode.SelectNodes(FetcherBinaryName);
            if (fetcherBinList == null)
            {
                log.FatalFormat("Unable to Get Fec Binaries  in App Config Section ");
                return false;
            }

            foreach (XmlNode fetcherBinNode in fetcherBinList)
            {
                XmlAttribute xmlAttr = null;

                xmlAttr = fetcherBinNode.Attributes[TypeName];
                if (xmlAttr == null)
                {
                    log.FatalFormat(" Missing Type Attribute ");
                    return false;
                }
                string typeStr = xmlAttr.Value;

                xmlAttr = fetcherBinNode.Attributes[DllName];
                if (xmlAttr == null)
                {
                    log.FatalFormat(" Missing DLL  Attribute ");
                    return false;
                }
                string dllStr = xmlAttr.Value;

                FetcherAndProcessorBin fetchObject = new FetcherAndProcessorBin(typeStr, dllStr);
                _fetcherBinConfigList[typeStr] = fetchObject;
            }


            #endregion

            #region READ_PROCESSOR_BIN

            XmlNodeList processorBinList = configNode.SelectNodes(ProcesorBinaryName);
            if (processorBinList == null)
            {
                log.FatalFormat("Unable to Get Processor  Binaries  in App Config Section ");
                return false;
            }

            foreach (XmlNode processorBinNode in processorBinList)
            {
                XmlAttribute xmlAttr = null;

                xmlAttr = processorBinNode.Attributes[TypeName];
                if (xmlAttr == null)
                {
                    log.FatalFormat(" Missing Type Attribute ");
                    return false;
                }
                string typeStr = xmlAttr.Value;

                xmlAttr = processorBinNode.Attributes[DllName];
                if (xmlAttr == null)
                {
                    log.FatalFormat(" Missing DLL  Attribute ");
                    return false;
                }
                string dllStr = xmlAttr.Value;

                FetcherAndProcessorBin processerObject = new FetcherAndProcessorBin(typeStr, dllStr);
                _processorBinConfigList[typeStr] = processerObject;
            }


            #endregion

            log.DebugFormat("Completed Reading General Configuration Section..");
            return retVal;
        }

        /// <summary>
        ///  Get Feature Bin 
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public FetcherAndProcessorBin GetFetcherBin(string key)
        {
            if ( (_fetcherBinConfigList!= null ) && (_fetcherBinConfigList.ContainsKey(key))  )
            {
                return _fetcherBinConfigList[key];
            }

            return null;
        }

        /// <summary>
        /// Get Processor Bin
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public FetcherAndProcessorBin GetProcessorBin(string key)
        {
        
            if ((_processorBinConfigList != null) && (_processorBinConfigList.ContainsKey(key)) )
            {
                return _processorBinConfigList[key];
            }

            return null;
        }


    }

    /// <summary>
    /// Application Configuration Object.
    /// </summary>
    public class AppConfig
    {
        private static readonly log4net.ILog log =
            log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        private List<FetcherConfigBaseRec> _fetcherRecords { get; set; }
        private Dictionary<string, List<ProcessorRecord>> _processorDictionary { get; set; }
        public GeneralConfig appgeneralConfig { get; private set; }

        private const string AppGeneralConfigName = "//AppGeneralConfig";
        private const string FetcherRecordConfigName = "//FetcherRecord";
        private const string ProcessorRecordConfigName = "//processor";

        private const string typeAttrStr = "type";

        public ReadOnlyCollection<FetcherConfigBaseRec> FetcherRecords
        {
            get
            {
                ReadOnlyCollection<FetcherConfigBaseRec> readOnlyCollection =
                    new ReadOnlyCollection<FetcherConfigBaseRec>(_fetcherRecords);
                return readOnlyCollection;
            }
        }

        public AppConfig()
        {
            _fetcherRecords = new List<FetcherConfigBaseRec>();
            _processorDictionary = new Dictionary<string, List<ProcessorRecord>>();
            appgeneralConfig = new GeneralConfig();
        }

        #region READ_STATIC_METHOD

        /// <summary>
        ///  Entry Mentod to read Confiuration.
        /// </summary>
        /// <param name="configFilePath"></param>
        /// <returns></returns>
        public static AppConfig readConfiguration(string configFilePath)
        {
            AppConfig _appConfig = null;
            XmlDocument xmlConfigDoc = new XmlDocument();

            try
            {
                xmlConfigDoc.Load(configFilePath);
                _appConfig = new AppConfig();

                if (!_appConfig.readConfiguration(xmlConfigDoc))
                {
                    log.DebugFormat("Configuration reading Failed :  " + configFilePath);
                    return null;
                }
            }
            catch (Exception e)
            {
                // Logging Error 
                Console.Error.WriteLine(" Exception while reading Config File " + configFilePath);
                Console.Error.WriteLine(e.StackTrace);
            }

            return _appConfig;
        }

        #endregion

        #region PROCESSOR_GET_BY_NAME

        public List<ProcessorRecord> getProcesserListAssociatedWithFetcher(string fetcherName)
        {
            if (_processorDictionary.ContainsKey(fetcherName))
            {
                return _processorDictionary[fetcherName];
            }

            return null;
        }

        #endregion

        private bool readConfiguration(XmlDocument xmlConfigDoc)
        {
            //1. Read App Specific Config 
            XmlNode generalConfigNode = xmlConfigDoc.SelectSingleNode(AppGeneralConfigName);
            if (!appgeneralConfig.LoadConfiguration(generalConfigNode))
            {
                log.DebugFormat(" Failed to Load General Configuration Section ");
                return false;
            }

            #region READING_FETCHER_RECORDS

            //2. Read Fetcher Records
            XmlNodeList fetcherList = xmlConfigDoc.SelectNodes(FetcherRecordConfigName);
            log.DebugFormat(" Number of Fetcher Records to read : " + fetcherList.Count);

            foreach (XmlNode fetcherNode in fetcherList)
            {
                XmlAttribute xmlAttr = null;

                Console.Write("Reading Fetcher Record  ");

                // read fetcher Name Attribute 
                xmlAttr = fetcherNode.Attributes[typeAttrStr];
                if (xmlAttr == null)
                {
                    log.FatalFormat("AttributeMissing : " + typeAttrStr + " :  For fetcher Record ");
                    return false;
                }

                FetcherAndProcessorBin fetchBinRec = appgeneralConfig.GetFetcherBin(xmlAttr.Value);
                FetcherConfigBaseRec fetcherCfgRec = FetcherConfigReader.GetConfiguration(fetchBinRec, fetcherNode);

                if (fetcherCfgRec.isActive)
                {
                    log.DebugFormat("Adding Active Fetcher Record {0}", fetcherCfgRec.FetcherName);
                    _fetcherRecords.Add(fetcherCfgRec);
                }
            }

            #endregion

            #region READING_PROCESSOR_RECORDS

            //2. Read Fetcher Records
            XmlNodeList processorList = xmlConfigDoc.SelectNodes(ProcessorRecordConfigName);
            log.DebugFormat(" Number of Fetcher Records to read : " + processorList.Count);

            foreach (XmlNode processorNode in processorList)
            {
                ProcessorRecord processerRec = new ProcessorRecord();

                // read fetcher Name Attribute 
                XmlAttribute xmlAttr = processorNode.Attributes[typeAttrStr];
                if (xmlAttr == null)
                    {
                    log.FatalFormat("AttributeMissing : " + typeAttrStr + " :  For Processor Record  Record ");
                    return false;
                    }

                FetcherAndProcessorBin processorBinRec = appgeneralConfig.GetProcessorBin(xmlAttr.Value);


                if (!processerRec.LoadConfiguration(processorBinRec,processorNode))
                {
                    log.DebugFormat(" Failed to read Processor record");
                    return false;
                }

                if (processerRec.IsActive)
                {
                    string[] fetchernames = processerRec.FetcherAssociation.Trim().Split(',');

                    foreach (string fetcherName in fetchernames)
                    {
                        List<ProcessorRecord> procRecs = null;

                        if (_processorDictionary.ContainsKey(fetcherName))
                        {
                            procRecs = _processorDictionary[fetcherName];
                        }
                        else
                        {
                            procRecs = new List<ProcessorRecord>();
                        }

                        procRecs.Add(processerRec);
                        _processorDictionary[fetcherName] = procRecs;
                    }
                }
            }

            #endregion

            return true;
        }
    }
}
